package com.widget;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextPaint;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.SoundEffectConstants;
import android.view.View;
import android.widget.OverScroller;

import androidx.annotation.NonNull;
import androidx.core.view.GestureDetectorCompat;
import androidx.core.view.ViewCompat;

import com.orhanobut.logger.Logger;
import com.somlsis.R;
import com.utils.DensityUtils;

public class WheelView extends View implements GestureDetector.OnGestureListener {
    //光标
    private int cursorColor = 0xFF3F8CFF;
    private float cursorSize = 10;
    private int cursorIndex;
    private Paint cursorPaint;
    private Path cursorPath = new Path();
    //刻度
    private float scaleHeight;
    private float scaleDownHeight;
    private int scaleStart = 0;
    private int scaleEnd = 30;
    private int scaleSize;
    private int scaleColor = 0xFF7E7E7E;
    private Paint scalePaint;
    private float scaleWidth = 2;
    private float scaleInterval = 5;
    private float scaleViewWidth;
    private float scaleRatio = 0.66f;
    private float scaleTextSize = 20;
    private int scaleTextColor = 0xFF333333;
    private Paint scaleTextPaint;
    //步长
    private int stepSize = 5;

    private float viewHeight;
    private float viewWidth;

    /*滑动相关 start*/
    private OverScroller mScroller;
    //最大滚动距离
    private float maxOverScrollDistance;
    private RectF rectF;
    private boolean mFling = false;
    private GestureDetectorCompat compat;
    private int lastCursorIndex = -1;
    /*滑动相关 end*/

    private int minSelectableIndex = Integer.MIN_VALUE;
    private int maxSelectableIndex = Integer.MAX_VALUE;

    private OnWheelItemSelectedListener listener;

    public WheelView(Context context) {
        super(context);
        init(null);
    }

    public WheelView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(attrs);
    }

    public WheelView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(attrs);
    }

    public interface OnWheelItemSelectedListener {
        void onWheelItemChanged(WheelView wheelView, int position);

        void onWheelItemSelected(WheelView wheelView, int position);
    }

    private void init(AttributeSet attrs) {
        float density = getResources().getDisplayMetrics().density;
        cursorSize = cursorSize * density;

        scaleWidth = scaleWidth * density;
        scaleInterval = scaleInterval * density;
        scaleTextSize = scaleTextSize * density;


        TypedArray ta = attrs == null ? null : getContext().obtainStyledAttributes(attrs, R.styleable.WheelView);

        cursorColor = ta.getColor(R.styleable.WheelView_cursorColor, cursorColor);
        cursorSize = ta.getDimension(R.styleable.WheelView_cursorSize, cursorSize);

        scaleStart = ta.getInt(R.styleable.WheelView_scaleStart, scaleStart);
        scaleEnd = ta.getInt(R.styleable.WheelView_scaleEnd, scaleEnd);
        scaleWidth = ta.getDimension(R.styleable.WheelView_scaleWidth, scaleWidth);
        scaleInterval = ta.getDimension(R.styleable.WheelView_scaleInterval, scaleInterval);
        scaleColor = ta.getColor(R.styleable.WheelView_scaleColor, scaleColor);
        scaleRatio = ta.getFloat(R.styleable.WheelView_scaleRatio, scaleRatio);
        scaleTextSize = ta.getDimension(R.styleable.WheelView_scaleTextSize, scaleTextSize);
        scaleTextColor = ta.getColor(R.styleable.WheelView_scaleTextColor, scaleTextColor);
        stepSize = ta.getInt(R.styleable.WheelView_stepSize, stepSize);

        scaleViewWidth = scaleInterval + scaleWidth;

        scaleSize = scaleEnd - scaleStart;
        cursorIndex = scaleSize / 2;
        minSelectableIndex = Math.max(minSelectableIndex, 0);
        maxSelectableIndex = Math.min(maxSelectableIndex, scaleSize);

        cursorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        cursorPaint.setTextAlign(Paint.Align.CENTER);
        cursorPaint.setColor(cursorColor);
        cursorPaint.setStrokeWidth(scaleWidth);

        scalePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        scalePaint.setTextAlign(Paint.Align.CENTER);
        scalePaint.setColor(scaleColor);
        scalePaint.setStrokeWidth(scaleWidth);

        scaleTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
        scaleTextPaint.setTextAlign(Paint.Align.CENTER);
        scaleTextPaint.setColor(scaleTextColor);
        scaleTextPaint.setTextSize(scaleTextSize);

        mScroller = new OverScroller(getContext());
        rectF = new RectF();

        compat = new GestureDetectorCompat(getContext(), this);
        minSelectableIndex = Math.max(minSelectableIndex, 0);
        maxSelectableIndex = Math.min(maxSelectableIndex, scaleSize);

    }

    public void setOnWheelItemSelectedListener(OnWheelItemSelectedListener onWheelItemSelectedListener) {
        listener = onWheelItemSelectedListener;
        selectIndex(cursorIndex);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        cursorPath.reset();
        cursorPath.moveTo(viewWidth / 2 + getScrollX(), viewHeight - cursorSize);
        cursorPath.lineTo((viewWidth - cursorSize) / 2 + getScrollX(), viewHeight);
        cursorPath.lineTo((viewWidth + cursorSize) / 2 + getScrollX(), viewHeight);
        cursorPath.close();
        canvas.drawPath(cursorPath, cursorPaint);

        for (int index = scaleStart; index <= scaleEnd; index++) {
            String value = String.valueOf(index);

            if (index-scaleStart == cursorIndex) {
                scalePaint.setColor(cursorColor);
                scaleTextPaint.setColor(cursorColor);
            } else {
                scalePaint.setColor(scaleColor);
                scaleTextPaint.setColor(scaleTextColor);
            }

            if (index == scaleStart || index % stepSize == 0) {
                canvas.drawLine(
                        scaleViewWidth * (index-scaleStart),
                        0,
                        scaleViewWidth * (index-scaleStart),
                        scaleHeight,
                        scalePaint);
                if (index-scaleStart != cursorIndex) {
                    canvas.drawText(value, scaleViewWidth * (index-scaleStart), viewHeight, scaleTextPaint);
                }

            } else {
                canvas.drawLine(
                        scaleViewWidth * (index-scaleStart),
                        (scaleHeight - scaleDownHeight) / 2,
                        scaleViewWidth * (index-scaleStart),
                        (scaleHeight - scaleDownHeight) / 2 + scaleDownHeight,
                        scalePaint);
            }

        }

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
    }

    private int measureWidth(int widthMeasureSpec) {
        int measureMode = MeasureSpec.getMode(widthMeasureSpec);
        int measureSize = MeasureSpec.getSize(widthMeasureSpec);
        int result = getSuggestedMinimumWidth();
        switch (measureMode) {
            case MeasureSpec.AT_MOST:
            case MeasureSpec.EXACTLY:
                result = measureSize;
                break;
            default:
                break;
        }
        return result;
    }

    private int measureHeight(int heightMeasure) {
        int measureMode = MeasureSpec.getMode(heightMeasure);
        int measureSize = MeasureSpec.getSize(heightMeasure);
        int result = (int) scaleTextSize;
        switch (measureMode) {
            case MeasureSpec.EXACTLY:
                result = Math.max(result, measureSize);
                break;
            case MeasureSpec.AT_MOST:
                result = Math.min(result, measureSize);
                break;
            default:
                break;
        }
        return result;
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        if (w != oldw || h != oldh) {
            viewWidth = w;
            viewHeight = h;
            scaleHeight = viewHeight - scaleTextSize;
            scaleDownHeight = scaleHeight * scaleRatio;
            maxOverScrollDistance = w / 2;
            rectF.set(0, 0, scaleViewWidth * scaleSize, h);
        }

    }


    @Override
    public boolean onDown(@NonNull MotionEvent e) {
        if (!mScroller.isFinished()) {
            mScroller.forceFinished(false);
        }
        mFling = false;
        if (null != getParent()) {
            getParent().requestDisallowInterceptTouchEvent(true);
        }
        return true;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        boolean ret = compat.onTouchEvent(event);
        if (!mFling && MotionEvent.ACTION_UP == event.getAction()) {
            autoSettle();
            ret = true;
        }
        return ret || super.onTouchEvent(event);
    }

    private void autoSettle() {
        int sx = getScrollX();
        float dx = cursorIndex * scaleViewWidth - sx - maxOverScrollDistance;
        mScroller.startScroll(sx, 0, (int) dx, 0);
        postInvalidate();
        if (lastCursorIndex != cursorIndex) {
            lastCursorIndex = cursorIndex;
            if (null != listener) {
                listener.onWheelItemSelected(this, cursorIndex+scaleStart);
            }
        }
    }

    @Override
    public boolean onScroll(@NonNull MotionEvent e1, @NonNull MotionEvent e2, float distanceX, float distanceY) {
        float dis = distanceX;
        float scrollX = getScrollX();

            scrollBy((int) dis, 0);
        refreshCenter();
        return true;
    }
    @Override
    public void computeScroll() {
        super.computeScroll();
        if (mScroller.computeScrollOffset()) {
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            refreshCenter();
            invalidate();
        } else {
            if (mFling) {
                mFling = false;
                autoSettle();
            }
        }
    }
    private void refreshCenter() {
        refreshCenter(getScrollX());
    }

    private void refreshCenter(int offsetX) {
        int offset = (int) (offsetX + maxOverScrollDistance);


        int tempIndex = Math.round(offset / scaleViewWidth);
        tempIndex = safeCenter(tempIndex);
        cursorIndex = tempIndex;
        if (null != listener) {
            listener.onWheelItemChanged(this, cursorIndex+scaleStart);
        }
    }

    private int safeCenter(int center) {
        if (center < minSelectableIndex) {
            center = minSelectableIndex;
        } else if (center >= maxSelectableIndex) {
            center = maxSelectableIndex;
        }
        return center;
    }

    @Override
    public boolean onFling(@NonNull MotionEvent e1, @NonNull MotionEvent e2, float velocityX, float velocityY) {
        float scrollX = getScrollX();
        if (scrollX < -maxOverScrollDistance  || scrollX > rectF.width() - maxOverScrollDistance) {
            return false;
        } else {
            mFling = true;
            fling((int) -velocityX, 0);
            return true;
        }
    }
    public void fling(int velocityX, int velocityY) {
        mScroller.fling(getScrollX(), getScrollY(),
                velocityX, velocityY,
                (int) (-maxOverScrollDistance ), (int) (rectF.width() - maxOverScrollDistance),
                0, 0,
                (int) maxOverScrollDistance, 0);
        ViewCompat.postInvalidateOnAnimation(this);
    }

    @Override
    public boolean onSingleTapUp(@NonNull MotionEvent e) {
        playSoundEffect(SoundEffectConstants.CLICK);
        refreshCenter((int) (getScrollX() + e.getX() - maxOverScrollDistance));
        autoSettle();
        return true;
    }
    public void selectIndex(int index) {
        cursorIndex = index;
        post(new Runnable() {
            @Override
            public void run() {
                scrollTo((int) (cursorIndex * scaleViewWidth - maxOverScrollDistance), 0);
                invalidate();
                refreshCenter();
            }
        });
    }
    @Override
    public void onLongPress(@NonNull MotionEvent e) {

    }

    @Override
    public void onShowPress(@NonNull MotionEvent e) {

    }

}
